package cn.ducis.macaw.core.staticflies.file;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import cn.ducis.macaw.util.basic.Assert;
import cn.ducis.macaw.util.types.StringUtil;
import cn.ducis.macaw.util.basic.PathUtil;
import cn.ducis.macaw.core.bPlusTree.*;

/**
 * @author Yellow
 * @version 1.0
 */
public class BlockFile {

	// private int detailCount;
	// private byte[] blockDetail;
	// 块文件存放的目录路径
	private static String blocksDir;
	// 用于暂存生成的块文件名，检查是否有重复
	private static ArrayList<String> blockNames = new ArrayList<String>();
	// 总的一级索引树
	private static BPlusTree inedxTree = new BPlusTree(3);
	// 块文件默认大小（暂设64MB）
	private static final int BLOCK_SIZE = 64 * 1024 * 1024;// 测试改小
	// 块文件后缀（暂定txt）
	private static final String BLOCK_SUFFIX = ".txt";
	// 随机生成的块文件名默认长度（暂设为8）
	private static final int BLOCK_NAME_LENTH = 8;

	/**
	 * 将传参文件路径中多个小文件的二进制码信息合并到一个总的字节码数组中，再将字节码数组的信息保存一个大的块文件
	 * 
	 * @param filePaths
	 *            存放小文件路径的字符串数组
	 * @param pathsCount
	 *            合并开始的序号
	 * @return 返回块文件存满后下一个小文件在路径数组中的序号
	 */
	public static int joinAtomFiles(String[] filePaths, int pathsCount) {
		// 确保存放小文件路径的数组非空
		Assert.notNull(filePaths, "Please initialzing filePaths array by it's setter.");
		// start end记录小文件在块文件中的位置 totalCount记录块文件存了多少字节小文件 record记录合并开始的序号
		int start, end, totalCount = 0, record = 0;
		// 整合小文件字节码信息的大文件数组
		byte[] block = new byte[BLOCK_SIZE];
		// 将图片的具体路径和其合并后在字节数组中的位置映射起来(K url,V int{start,end})
		Map<String, String> fileMap = new HashMap<String, String>();

		// 生成块文件名和存放块文件的目录
		String blockName = blockName();
		String blockDir = generateDir(blocksDir);
		// 遍历存放所有文件路径的数组，将文件二进制信息依次存到数组中
		for (int index = pathsCount; index <= filePaths.length; index++) {
			// 文件夹中最后一个文件特殊处理，因为通常最后一个文件的end <= blockSize，导致无法进到else记录结束位置，pathsCount不正确死循环
			if (index == filePaths.length) {
				record = index;
				break;
			}

			byte[] temp = FileParse.fileToArray(filePaths[index]);
			start = totalCount;
			end = start + temp.length;
			// end <= blockSize最后一个文件要能存进数组（会有内部碎片：开辟了64M的空间却没存满）
			if (end <= BLOCK_SIZE) {
				for (totalCount = start; totalCount < end; totalCount++) {
					block[totalCount] = temp[totalCount - start];
				}
				String mapValue = MapValue.valueMerge(start, end, blockName, blockDir);
				// 将文件路径与其在合成的大文件数组中的位置以及对应块信息映射起来组成键值对（单个文件块Map）
				fileMap.put(filePaths[index], mapValue);
				// 将小文件与map索引文件路径插入到索引树中
				inedxTree.insertOrUpdate(filePaths[index], Index.getMapsDir() + "/" + blockName + Index.getMapSuffix());
			} else {
				// 记录循环结束的位置
				record = index;
				break;
			}
		}
//		StaticFileUtil.getFileMaps().putAll(fileMap);
		// 将块Map存放在指定目录下，并且名称与块相同，但后缀不同
		Index.storeFileMap(fileMap, blockName);
		// 存放块文件到指定路径
		String blockPath = blockDir + "/" + blockName + BLOCK_SUFFIX;
		FileParse.writeFile(block, blockPath);
		return record;
	}

	/**
	 * 向块文件中增加小文件（以增操作代替改操作）
	 * 
	 * @param atomFilePath
	 *            小文件路径
	 * @param blockFilePath
	 *            块文件路径
	 */
	public static void addAtomFile(String atomFilePath, String blockFilePath) {
		// TODO
	}

	/**
	 * 计算指定块文件中的剩余空间
	 * 
	 * @return
	 */
	public static int remainSpace() {
		// TODO
		
		return 0;
	}

	/**
	 * 随机生成固定长度的字符串最为块文件的文件名
	 * 
	 * @return
	 */
	private static String blockName() {
		String blockName;
		do {
			blockName = StringUtil.generateRandomString(BLOCK_NAME_LENTH);
		} while (blockNames.contains(blockName));// 名称已存在则重新生成

		blockNames.add(blockName);
		return blockName;
	}

	/**
	 * 根据传参字符串创建目录
	 * 
	 * @return 返回指定目录字符串
	 */
	public static String generateDir(String dirStr) {
		File dir = new File(dirStr);
		if (!dir.exists()) {
			dir.mkdirs();
		}
		return PathUtil.formatPathString(dir.getAbsolutePath());
	}

	public static String getBlocksDir() {
		return blocksDir;
	}

	public static void setBlocksDir(String blocksDir) {
		BlockFile.blocksDir = blocksDir;
	}

	public static BPlusTree getInedxTree() {
		return inedxTree;
	}

	public static String getBlocksuffix() {
		return BLOCK_SUFFIX;
	}

	public static int getBlocknamelenth() {
		return BLOCK_NAME_LENTH;
	}

}
